#ifndef ODBC_DATA_READER_HPP
#define ODBC_DATA_READER_HPP

#include "odbc/details/bindable_buffer_base.hpp"
#include "odbc/details/statement_visitor.hpp"
#include "odbc/details/column_buffer.hpp"
#include <boost/tuple/tuple.hpp>
#include <boost/noncopyable.hpp>
#include <boost/foreach.hpp>
#include <vector>
#include <list>

namespace odbc
{

/// \ingroup ODBCLibrary
/// \brief 
///		It provides an STL like interface for forward iterating through the results of a query.
/// \param T
///		The boost::tuple type, providing the data types for each column in the resulted record set.
/// \details
///		Before initializing the data reader, make sure that the statement used for initialization is in execute mode, 
///		so the column binding is successful. If the statement is not executed before the data reader initialization, an 
///		ODBC error will be thrown in the constructor.
///		\n
///		When iterating, the iterator provides a reference to the current record, which is a vector of boost::any values. 
///		Check for emptiness of a value in the current record before performing a boost::any_cast to the real type.
template< class T>
class ODBC_DECL data_reader: details::statement_visitor, boost::noncopyable
{
	typedef T tuple_type;
friend class statement;
public:
	typedef std::vector< boost::any> record_type;

	/// \brief It binds the columns to the statement.
	/// \param statToRead the statement to bind the reader to.
	/// \exception odbc_error in case of error during binding.
	/// \pre The statement is in execution state (the statement::execute method was called before the initialization of 
	///		the reader).
	data_reader( statement& statToRead)
		: current_record_( boost::tuples::length< tuple_type>::value)
		, stat_to_read_( statToRead)
	{
		SQLRETURN retCode = SQLNumResultCols( get_statement_handle( statToRead), &noOfColumns_);
		odbc_error::check_success( retCode, statToRead);

		column_binder<> binder( statToRead, buffers_);
		binder;
	}

	/// \brief It destroys the bound buffers before destroying the reader.
	~data_reader()
	{
		BOOST_FOREACH( details::bindable_buffer_base* p_buffer, buffers_)
		{
			delete p_buffer;
		}
	}

	/// \brief It gets the number of the columns resulted from statement execution.
	unsigned int numberOfColumns() const { return static_cast< unsigned int>( noOfColumns_); }

private:
	/// \internal
	/// \brief It contains the buffers bound for fetching the data.
	std::vector< details::bindable_buffer_base*> buffers_;
	
	/// \internal
	/// \brief It refers to the statement to be executed.
	statement& stat_to_read_;

	/// \internal
	/// \brief It contains the number of output columns the statement produced after execution.
	SQLSMALLINT noOfColumns_;

	/// \internal
	/// \brief It is the current record.
	/// \details
	///		When fetching, it contains the data of the current row.
	///		\n
	///		It is accessible only through the iterators.
	record_type current_record_;

	/// \internal
	/// \brief It fetches the data of the next record.
	/// \return true if there is a record to be fetched or false otherwise.
	bool fetch()
	{
		SQLRETURN fetchResultCode = SQLFetch( get_statement_handle( stat_to_read_));
		if( fetchResultCode != SQL_NO_DATA)
		{
			// At this point, either some data was successfully fetched or some 
			//	error occurred. Check if any error.
			odbc_error::check_success( fetchResultCode, stat_to_read_);
			// Successful data fetched. 
			// Put it in the current record
			int fieldIndex = 0;
			BOOST_FOREACH( details::bindable_buffer_base* p_buffer, buffers_)
			{
				current_record_[fieldIndex++] = p_buffer->value();
			}
			return true;
		}
		else
		{
			// No more records. 
			return false;
		}
	}

public:
	/// \ingroup OdbcLibrary
	/// \brief
	///		It provides the intuitive way of iterating through the data set, without modifying the iterated record 
	///		object.
	/// \details
	///		The iterated element (\c value_type) is the \c \link record \endlink element.
	///		\n
	///		A valid iterator is the one having a not null dataset statement reference. An \c end iterator is the one 
	///		having a null dataset statement reference.
	class ODBC_DECL const_iterator
		: public std::iterator< std::forward_iterator_tag, record_type, std::size_t, std::size_t, record_type&>
	{
	public:
		/// \brief
		///		It initializes a valid iterator, using the specified dataset 
		///		statement.
		/// \param stat the dataset statement providing the data.
		/// \details
		///		It tries to fetch the first record, so it contains valid data in case of valid iterator. However, if no 
		///		data is available for fetching, the associated dataset statement will be set to NULL and the iterator 
		///		will look like an \c end iterator.
		const_iterator( data_reader& stat)
			: p_reader_( stat.fetch() ? &stat : NULL)
		{
		}


		///	\brief
		///		It initializes an \c end iterator.
		const_iterator()
			: p_reader_( NULL)
		{
		}

		/// \brief
		///		It fetches the next record from the source dataset.
		/// \details
		///		If there is no record to be fetched, the iterator becomes an \c end iterator.
		const_iterator& operator++( int)
		{
			p_reader_ = p_reader_->fetch() ? p_reader_ : NULL;
			return *this;
		}

		/// \brief
		///		It fetches the next record from the source dataset.
		/// \details
		///		If there is no record to be fetched, the iterator becomes an \c end iterator.
		const_iterator operator++()
		{
			const_iterator cpy( *this);
			p_reader_ = p_reader_->fetch() ? p_reader_ : NULL;
			return cpy;
		}

		///	\brief
		///		It compares two iterators.
		/// \return
		///		true if the two iterators are equal or false otherwise.
		/// \note
		///		Due to the limitations on iterations (see \c \link dataset_statement \endlink class documentation), two 
		///		iterators are equal if they refer to the same dataset.
		///		\n
		///		Two \c end iterators are always equal, since they refer to a null dataset statement reference.
		bool operator==( const const_iterator& op2) const
		{
			return p_reader_ == op2.p_reader_;
		}

		///	\brief
		///		It compares two iterators.
		/// \return
		///		true if the two iterators are equal or false otherwise.
		/// \note
		///		Due to the limitations on iterations (see \c \link dataset_statement \endlink class documentation), two 
		///		iterators are equal if they refer to the same dataset.
		///		\n
		///		Two \c end iterators are always equal, since they refer to a null dataset statement reference.
		bool operator!=( const const_iterator& op2) const
		{
			return !(this->operator==( op2));
		}

		///	\brief
		///		It gets the current record.
		/// \return
		///		the current record.
		/// \note 
		///		This method will crash if called for an \c end iterator.
		const value_type& operator*() const
		{
			return p_reader_->current_record_;
		}

		///	\brief
		///		It gets the current record.
		/// \return
		///		the current record.
		/// \note 
		///		This method will crash if called for an \c end iterator.
		const value_type& operator->() const
		{
			return p_reader_->current_record_;
		}

	protected:
		/// \internal
		///	\brief It contains the associated statement, giving the dataset to be iterated. This pointer is \c NULL for 
		///		\c end iterators.
		mutable data_reader* p_reader_;
	};



	/// \brief
	///		It starts the iteration of the dataset, by executing it and fetching the first record.
	///	\return 
	///		An iterator pointing to the first record of the dataset, or an \c end iterator if no record was available 
	///		for fetching.
	/// \note
	///		It closes the previous cursor, if any, so it can support reopening of the statement even if it was not c
	///		ompletely iterated.
	/// \exception odbc_error 
	///		Thrown if there was an error during execution of the statement or fetching the first record of the dataset.
	const_iterator begin() const
	{
		return const_iterator( const_cast< data_reader&>( *this));
	}

	/// \brief
	///		It gets the iterator indicating the end of the dataset.
	/// \return
	///		the \c end iterator of the dataset.
	const_iterator end() const
	{
		return const_iterator();
	}

private:
	/// \internal
	///	\brief Utility method for creating the column buffer appropriate for the given data type.
	/// \param T 
	///		the type of the data to created the column buffer for.
	/// \param statToRead
	///		the statement to bind the buffer to.
	/// \param buffers
	///		the collection to add the newly created and bound column buffer.
	/// \param index
	///		the index of the column to be bound inside the record (1 based).
	template< class T>
	static void bind_buffer( 
			statement& statToRead, std::vector< details::bindable_buffer_base*>& buffers, unsigned short index)
	{
		std::auto_ptr< details::bindable_buffer_base> p_buffer( new details::column_buffer< T>);
		p_buffer->bind( statToRead, SQLUSMALLINT( index));
		buffers.push_back( p_buffer.release());
	}

	/// \internal
	/// \brief 
	///		It binds the column corresponding to the index given as template parameter.
	/// \param Index
	///		The index of the column to bind the buffer for. The index is also used at compile time to get the type of 
	///		the data for the column to be bound, from the tuple type parameter of the parent class (data_reader).
	/// \details
	///		This class provides a compile time code generation for binding column buffers corresponding to the types in 
	///		the tuple the data_reader class is parameterized with.
	///		\n
	///		The class is derived from the same class, parameterized with a decremented index, and the buffer binding is 
	///		performed in the constructor. This way, the first binding is for column 1.
	template< unsigned short Index = boost::tuples::length<tuple_type>::value - 1>
	class column_binder: column_binder<Index - 1>
	{
		typedef column_binder< Index - 1> base_class;
	public:
		/// \brief It binds the column to the statement. The position of the column is given by the index template 
		///		parameter.
		/// \param statToRead
		///		the statement to bind the buffer to.
		/// \param buffers
		///		the collection to add the newly created and bound column buffer. The buffer collection is actually the 
		///		data member from data_reader outer class.
		column_binder( statement& statToRead, std::vector< details::bindable_buffer_base*>& buffers)
			: base_class( statToRead, buffers)
		{
			typedef boost::tuples::element<Index, tuple_type>::type data_type;
			bind_buffer< data_type>( statToRead, buffers, Index + 1);
		}
	};

	/// \internal
	/// \brief End of compile time recursion for column binding.
	template<>
	class column_binder< 0>
	{
	public:
		column_binder( statement& statToRead, std::vector< details::bindable_buffer_base*>& buffers)
		{
			typedef boost::tuples::element<0, tuple_type>::type data_type;
			bind_buffer< data_type>( statToRead, buffers, 1);
		}
	};
};


} // namespace odbc

#endif // ODBC_DATA_READER_HPP
